package com.robusta.cityuate.services;

import java.io.IOException;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Set;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import android.app.IntentService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;

import com.robusta.cityuate.CityuateApplication;
import com.robusta.cityuate.R;
import com.robusta.cityuate.activities.LoginActivity;
import com.robusta.cityuate.utils.ConnectionUtils;
import com.robusta.cityuate.utils.Token;

abstract public class HTTPService extends IntentService {

	public static final String KEY_TYPE = "type";
	// public static final String KEY_URL = "URL";
	// public static final String KEY_BODY = "BODY";
	public static final String RESUlT = "result";
	public static final String AUTH_TOKEN = "auth_token";
	public static final String LOGIN_RQUIRED = "login_required";
	public static final String MESSAGE = "message";
	public static final String Error = "error";
	public static final String CANCEL = "cancel";
	public static final String UNKNOWN_RESPONSE = "Unknown Server Response";

	// private static final String HTTP_RESPONSE = "HTTP_RESPONSE";
	// private static final String HTTP_STATUS = "HTTP_STATUS";

	private static final String TAG = HTTPService.class.getName();

	public static final String SERVER_URL = "http://cityuate.herokuapp.com";
	// public static final String SERVER_URL = "http://shanab.local:3000";
	public static final String API_URL = SERVER_URL + "/api";
	// private String uri = "";
	private HttpRequestBase request;
	private HashMap<String, String> queryParameters = new HashMap<String, String>();

	// private int type = 0;
	// public static final int GET_REQUEST = 0;
	// public static final int POST_REQUEST = 1;
	// public static final int PUT_REQUEST = 2;
	// public static final int DELETE_REQUEST = 3;

	DefaultHttpClient client = new DefaultHttpClient();
	HttpResponse response = null;
	protected Bundle extras = null;
	protected String responseString = null;
	protected int responseStatusCode;
	protected String assignedFilter;
	protected String body = "";
	private IntentService mContext = this;
	private boolean needsLogin;
	private boolean isCancel = false;

	private BroadcastReceiver cancelReceiver;

	public HTTPService() {
		super("HTTPService");
	}

	/**
	 * Executes HTTP requests and broadcasts an intent containing a String
	 * representing the HTTP response, the HTTP response string key in the
	 * broadcasted intent is <code>HTTP_RESPONSE</code> string defined in
	 * <code>AppConstants</code> class, so to get it just use: String
	 * responseStr = intent.getExtras().getString(HTTP_RESPONSE);The action of
	 * the broadcasted intent is the same as the action in the intent parameter
	 * passed (<code>intent</code>). So each activity should have something
	 * like: final String HTTP_<This_Activity>_FILTER="blabla"; and add this
	 * string in the service intent like the following:
	 * serviceIntent.add(FILTER,HTTP_<This_Activity>_FILTER); FILTER is a string
	 * constant defined in AppConstants class.
	 * 
	 * @param intent
	 *            the intent passed from the caller activity
	 */

	@Override
	protected void onHandleIntent(Intent intent) {
		// Get the filter assigned to the intent from the caller activity
		assignedFilter = intent.getAction();
		isCancel = false;
		cancelReceiver = new BroadcastReceiver() {

			@Override
			public void onReceive(Context arg0, Intent arg1) {
				Log.e("HTTPService", assignedFilter + " " + CANCEL);
				isCancel = true;
				client.getConnectionManager().shutdown();
				// stopSelf();
			}
		};

		registerReceiver(cancelReceiver, new IntentFilter(assignedFilter
				+ CANCEL));

		Log.d(TAG, "AssignedFilter0= " + assignedFilter);

		ConnectionUtils.setConnecting(mContext, assignedFilter);

		// handle the specific service requirements
		// handleSpecificRequirments(intent);

		// getting extras attached to the intent
		extras = intent.getExtras();
		Log.e("Extras", extras.toString());

		// if it needs login
		if (extras.containsKey(LOGIN_RQUIRED)) {
			needsLogin = extras.getBoolean(LOGIN_RQUIRED, false);
			extras.remove(LOGIN_RQUIRED);
		}

		// Create HTTP Request.
		request = initRequest();
		// Adding required Cityuate server headers
		setCityuateHeaders();

		// add token if login required
		if (needsLogin)
			addToken();
		// add passed paramteres including token
		buildURI();
		try {
			Log.e("Request", request.getURI().toString());
			// Executes the connection
			if (!ConnectionUtils.isNetworkAvailable(mContext))
				throw new Exception(getString(R.string.server_unreachable));
			response = client.execute(request);

			responseStatusCode = response.getStatusLine().getStatusCode();
			Log.d(TAG, responseStatusCode + "");
			if (responseStatusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR)
				throw new Exception("Server Error");
			if (response.getEntity() != null)
				responseString = EntityUtils.toString(response.getEntity());
			else
				responseString = "none";

			Log.d(TAG, responseString);

			if (responseStatusCode == HttpStatus.SC_UNAUTHORIZED) {
				JSONObject msg = new JSONObject(responseString);
				
				if (msg.has(Error)) {
					onNoResponse(getString(R.string.please_login));
					Token.removeToken(this);
					kickout();
					return;
				}
			}

			// if it was able to connect to server and a response received
			onResponse();

		} catch (SocketException e) {
			
			 e.printStackTrace();
			if (isCancel) {
				Intent broadcastCanel = new Intent(assignedFilter);
				broadcastCanel.putExtra(CANCEL, true);
				sendBroadcast(broadcastCanel);
			} else
				onNoResponse(getString(R.string.server_unreachable));
			// unregisterReceiver(cancelReceiver);
		}

		catch (IOException e) {
			Log.e(TAG, " IO exc");
			// e.printStackTrace();
			if (isCancel) {
				Intent broadcastCanel = new Intent(assignedFilter);

				broadcastCanel.putExtra(CANCEL, true);
				sendBroadcast(broadcastCanel);
			} else
				onNoResponse(getString(R.string.server_unreachable));
			// unregisterReceiver(cancelReceiver);
		}

		catch (Exception e) {

			Log.e(TAG, "Error " + e.getMessage());
			onNoResponse(getString(R.string.server_unreachable));

		}
		unregisterReceiver(cancelReceiver);
		// stopSelf();
	}

	// /**
	// * To be implemented for specific cases required by the childern service
	// *
	// * @param intent
	// */
	// abstract protected void handleSpecificRequirments(Intent intent);

	/**
	 * If the response was received from the server so it's time to check if
	 * it's ok or not and apply required scenarios
	 */
	abstract protected void onResponse();

	/**
	 * in Case of failure to reach the server
	 */
	abstract protected void onNoResponse(String msg);

	/**
	 * should be overridden to create the request according to required
	 * 
	 * @return
	 */
	abstract protected HttpRequestBase initRequest();

	/**
	 * Prepares the http header required by cityaute server.
	 * 
	 * @return
	 */
	private void setCityuateHeaders() {
		request.setHeader("Accept", "vnd.cityuate.v1");
		request.setHeader("Content-type", "application/json");
	}

	/**
	 * adds token to request that needs login
	 */
	private void addToken() {
		// String uri = request.getURI().toString();

		String token = Token.getToken(this);
	

		if (token == null)
			{onNoResponse(getString(R.string.please_login));
			kickout();
			}
		else
		// uri += ("?" + AUTH_TOKEN + "=" + token);
			addPrameter(AUTH_TOKEN, token);
		// Log.e("URI",uri);
		// try {
		// request.setURI(new URI(uri));
		// } catch (URISyntaxException e) {
		// e.printStackTrace();
		// }
	}

	public void addPrameter(String key, String value) {
		queryParameters.put(key, value);
	}

	public void buildURI() {
		String uri = request.getURI().toString();
		uri += "?";
		Set<String> keys = queryParameters.keySet();
		for (String key : keys) {
			String v = queryParameters.get(key);
			uri += key + "=" + v + "&";
		}
		uri = uri.substring(0, uri.length() - 1);

		try {
			request.setURI(new URI(uri));
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}

	public void kickout(){
		Intent out = new Intent(CityuateApplication.CLOSE_TAG);
		sendOrderedBroadcast(out, null);
		Intent act=new Intent(mContext, LoginActivity.class);
		act.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		act.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
		mContext.startActivity(act);
		
	}
	
}
